home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / C / VT100_10.ZIP / VT100.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-11  |  31.7 KB  |  1,125 lines

  1. /* vt100.c
  2.  *
  3.  * AUTHOR:  John L. Miller, johnmil@cs.cmu.edu / johnmil@jprc.com
  4.  * DATE:    8/4/96
  5.  *
  6.  * Copyright (c) 1996 John L. Miller
  7.  *
  8.  * Full permission is granted to use, modify and distribute
  9.  * this code, provided:
  10.  * 1) This comment field is included in its entirity
  11.  * 2) No money is charged for any work including or based on 
  12.  *    portions of this code.
  13.  *
  14.  * If you're a nice person and find this useful, I'd appreciate a
  15.  * note letting me know about it. e-mail is usually what spurs me
  16.  * on to improve and support software I've written.
  17.  *
  18.  */
  19.  
  20. /* This is the main code body for my generic vt-100 emulator. This code
  21.  * body provides parsing for most of the vt-100 escape sequences, but it
  22.  * doesn't actually do anything with some of them. The idea behind this 
  23.  * parser is to provide a generic front-end that you can initialize, then
  24.  * send all of your output to. The output is parsed by the routines in this
  25.  * program, then spit out to a back-end.
  26.  *
  27.  * What back-end you say? Well, the one you have to supply yourself. There's a
  28.  * dozen or so routines you have to provide for character-based I/O, cursor 
  29.  * movement, erasing and deleting text, and setting text and terminal attributes.
  30.  *
  31.  * For a list of the routines your back end must supply, read vt100.h closely.
  32.  *
  33.  * In case it's not obvious, these routines were written for a system running win32.
  34.  * for vt100.c and vt100.h, most of the code should be easily portable to other
  35.  * operating systems. Yeah, like they NEED a vt-100 emulator :p
  36.  */
  37.  
  38. #include <windows.h>
  39. #include <stdio.h>
  40. #include <stdarg.h>
  41. #include "vt100.h"
  42.   
  43.  
  44. /*  NOTE - many of the functions look like they should
  45.  * take X-Y pairs. Bear in mind this is a text display,
  46.  * so for this we're talking ROWS and COLUMNS. So parm
  47.  * lists go (row, column), which is the opposite of (x, y).
  48.  */
  49.  
  50. char    cBuffer[MAXVTBUFLEN+1]; /* terminal output buffer for unprocessed characters */
  51. int     BufLen;                 /* Number of characters in cBuffer waiting for output */
  52.  
  53. /* List of all device-independant colors. These colors will be transmitted to a
  54.  * back-end routine responsible for setting the foreground and background text
  55.  * colors appropriately. Note that the color combinations are ordered to correspond
  56.  * with combinations of red (0x1), green (0x2) and blue (0x4) bit flags.
  57.  */
  58.  
  59. int ScColorTrans[8]= { 0,
  60.                     SC_RED,
  61.                     SC_GREEN,
  62.                     SC_RED|SC_GREEN,
  63.                     SC_BLUE,
  64.                     SC_RED|SC_BLUE,
  65.                     SC_RED|SC_GREEN,
  66.                     SC_RED|SC_GREEN|SC_BLUE
  67.                     };
  68.  
  69.  
  70. /* List of terminal attributes which we track (used by <esc>[?#h and <esc>[?#l) */
  71.  
  72. int termAttrMode[NUM_TERM_ATTR_MODES] = { 0,
  73.                     CURSORAPPL_MODE,
  74.                     ANSI_MODE,
  75.                     COL132_MODE,
  76.                     SMOOTHSCROLL_MODE,
  77.                     REVSCREEN_MODE,
  78.                     ORIGINREL_MODE,
  79.                     WRAP_MODE,
  80.                     REPEAT_MODE,
  81.                     };
  82.  
  83. /* FORWARD FUNCTION DECLARATIONS -
  84.  * these functions are intended for use only in this module */
  85.  
  86. static int ProcessBracket(int Start);
  87. static int ProcessEscape(int Start);
  88. static int ProcessControl(int Start);
  89. static int ProcessBuffer(void);
  90.  
  91. /* END FORWARD FUNCTION DECLARATIONS */
  92.  
  93.  
  94.  
  95. /* vtputs() - 
  96.  * 
  97.  * front-end 'puts()' substitute. Call this routine instead 
  98.  * of 'puts()', and it'll pass the output through the vt100 emulator.
  99.  */
  100.  
  101. vtputs(char *f)
  102. {
  103.     char cbuf[1024];
  104.  
  105.     strcpy(cbuf,f);
  106.     strcat(cbuf,"\n");
  107.     vtProcessedTextOut(cbuf, strlen(cbuf));
  108.  
  109.     return(0);
  110. }
  111.  
  112.  
  113. /* vtprintf -
  114.  * 
  115.  * This routine is a substitute for printf(). Call this routine with the 
  116.  * same parameters you would use for printf, and output will be channelled
  117.  * through the vt-100 emulator.
  118.  */
  119.  
  120. vtprintf(char *format, ...)
  121. {
  122.     char cbuf[1024];
  123.     va_list va;
  124.  
  125.     va_start(va, format);
  126.     vsprintf(cbuf,format, va);
  127.     va_end(va);
  128.  
  129.     vtProcessedTextOut(cbuf, strlen(cbuf));
  130.  
  131.     return(0);
  132. }
  133.  
  134. /* vtInitVT100 -
  135.  *
  136.  * Set the initial state of the VT-100 emulator, and call the initialization
  137.  * routine for the back end. This routine MUST be invoked before any other, or 
  138.  * the VT-100 emulator will most likely roll over and die.
  139.  */
  140.  
  141. vtInitVT100(void)
  142. {
  143.     int i=0;
  144.  
  145.     cBuffer[0]='\0';
  146.     BufLen=0;
  147.  
  148.     beInitVT100Terminal(); /* call the back-end initialization. */
  149.  
  150.     return(0);
  151. }
  152.  
  153.  
  154. /* ProcessBracket -
  155.  * 
  156.  * Helper routine for processing escape sequences. By the time this
  157.  * routine is invoked, '<esc>[' has already been read in the input
  158.  * stream. 'Start' is an index in cBuffer to the '<esc>'.
  159.  *
  160.  * If an escape sequence is successfully parsed, return the index of the
  161.  * first character AFTER the escape sequence. Otherwise, return 'Start'.
  162.  *
  163.  */
  164.  
  165. static int ProcessBracket(int Start)
  166. {
  167.     int End;                 /* Current character being examined in cBuffer */
  168.     int args[10], numargs=0; /* numerical args after <esc>[ */
  169.     int iMode;               
  170.     int i;
  171.     int itmp=0;
  172.     int left;
  173.     int iForeground, iBackground;
  174.  
  175.     /* If there's no valid escape sequence, return as we were called. */
  176.  
  177.     if ((cBuffer[Start+1] != '[')||(Start+2 >= BufLen))
  178.         return(Start);
  179.  
  180.     End = Start + 2;
  181.  
  182.     /* Loop through the buffer, hacking out all numeric
  183.      * arguments (consecutive string of digits and
  184.      * semicolons
  185.      */
  186.  
  187.     do {
  188.         itmp = 0; /* itmp will hold the current arguments integer value */
  189.  
  190.                   /* the semicolon is a delimiter */
  191.         if (cBuffer[End] == ';')
  192.         {
  193.             End++;
  194.  
  195.             if (End >= BufLen)
  196.                 return(Start);
  197.         }
  198.  
  199.         /*  Parse this argument into a number. */
  200.  
  201.         while (isdigit(cBuffer[End]))
  202.         {
  203.             itmp = itmp*10 + (cBuffer[End]-'0');
  204.             End++;
  205.             if (End >= BufLen)
  206.                 return(Start);
  207.         }
  208.  
  209.         /* Save the numeric argument if we actually
  210.          * parsed a number.
  211.          */
  212.  
  213.         if (End != Start + 2)
  214.             args[numargs++] = itmp;
  215.  
  216.     } while (cBuffer[End] == ';');
  217.  
  218.     /* At this point, we've come across a character that isn't a number
  219.      * and isn't a semicolon. This means it is the command specifier.
  220.      */
  221.  
  222.     /* Number of characters left in the input stream. I don't use 
  223.      * this as rigorously as I should here.
  224.      */
  225.  
  226.     left =  BufLen - End;
  227.  
  228.     /* Return if there's definitely not enough characters for a
  229.      * full escape sequence.
  230.      */
  231.  
  232.     if (left <= 0)
  233.         return(Start);
  234.  
  235.     /* Giant switch statement, parsing the command specifier that followed
  236.      * up <esc>[arg;arg;...arg
  237.      */
  238.  
  239.     switch (cBuffer[End])
  240.     {
  241.     /*         Cursor Up:      Esc [ Pn A */
  242.     case 'A':
  243.         beOffsetCursor(numargs ? -args[0] : -1, 0);
  244.         End += 1;
  245.         break;
  246.  
  247.     /*         Cursor Down:    Esc [ Pn B */
  248.     case 'B':
  249.         beOffsetCursor(numargs ? args[0] : 1, 0);
  250.         End += 1;
  251.         break;
  252.  
  253.     /*         Cursor Right:    Esc [ Pn C */
  254.     case 'C':
  255.         beOffsetCursor(0, numargs ? args[0] : 1);
  256.         End += 1;
  257.         break;
  258.  
  259.     /*          Cursor Left:   Esc [ Pn D */
  260.     case 'D':
  261.         beOffsetCursor(0, numargs ? -args[0] : -1);
  262.         End += 1;
  263.         break;
  264.  
  265.     /*          Direct Addressing :  Esc [ Pn(row);Pn(col)H or
  266.      *                               Esc [ Pn(row);Pn(col)f
  267.      */
  268.     case 'H':
  269.     case 'f':
  270.         if (numargs == 0)
  271.             beAbsoluteCursor(1,1);
  272.         else if (numargs == 1)
  273.             beAbsoluteCursor(args[0] > 0 ? args[0] : 1,1);
  274.         else if (numargs == 2)
  275.             beAbsoluteCursor(args[0] > 0 ? args[0] : 1, args[1] > 0 ? args[1] : 1);
  276.  
  277.         End += 1;
  278.         break;
  279.  
  280.     /*         Erase from Cursor to end of screen         Esc [ J
  281.      *         Erase from Beginning of screen to cursor   Esc [ 1 J
  282.      *         Erase Entire screen                        Esc [ 2 J
  283.      */
  284.     case 'J':
  285.         if (numargs == 0)
  286.             beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE);
  287.         else if (args[0] == 1)
  288.             beEraseText(TOP_EDGE, LEFT_EDGE, CUR_ROW, CUR_COL);
  289.         else
  290.             beEraseText(TOP_EDGE, LEFT_EDGE, BOTTOM_EDGE, RIGHT_EDGE);
  291.  
  292.         End += 1;
  293.         break;
  294.  
  295.     /*         Erase from Cursor to end of line           Esc [ K
  296.      *         Erase from Beginning of line to cursor     Esc [ 1 K
  297.      *         Erase Entire line                          Esc [ 2 K
  298.      */
  299.     case 'K':
  300.         if (numargs == 0)
  301.             beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE);
  302.         else if (args[0] == 1)
  303.             beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, CUR_COL);
  304.         else
  305.             beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, RIGHT_EDGE);
  306.  
  307.         End += 1;
  308.         break;
  309.  
  310.  
  311.     /*  Set Graphics Rendition:
  312.      *          ESC[#;#;....;#m
  313.      *  The graphics rendition is basically foreground and background
  314.      *  color and intensity.
  315.      */
  316.     case 'm':
  317.         beGetTextAttributes(&iForeground, &iBackground);
  318.  
  319.         if (numargs < 1)
  320.         {
  321.             /* If we just get ESC[m, treat it as though
  322.              * we should shut off all extra text
  323.              * attributes
  324.              */
  325.  
  326.             iForeground &= ~(SC_BOLD|SC_UL|SC_BL|SC_RV|SC_GRAPHICS|SC_G0|SC_G1);
  327.             iForeground |= SC_ASCII;
  328.  
  329.             beSetTextAttributes(iForeground, iBackground);
  330.             End += 1;
  331.             break;
  332.         }
  333.  
  334.         /* Loop through all the color specs we got, and combine them
  335.          * together. Note that things like 'reverse video', 'bold',
  336.          * and 'blink' are only applied to the foreground. The back end
  337.          * is responsible for applying these properties to all text.
  338.          */
  339.         for (i=0; i < numargs; i++)
  340.         {
  341.             switch(args[i])
  342.             {
  343.             /* 0 for normal display */
  344.             case 0:
  345.                 iForeground &= ~SC_BOLD;
  346.                 break;
  347.  
  348.             /* 1 for bold on */
  349.             case 1:
  350.                 iForeground |= SC_BOLD;
  351.                 break;
  352.  
  353.             /* 4 underline (mono only) */
  354.             case 4:
  355.                 iForeground |= SC_UL;
  356.                 break;
  357.  
  358.             /* 5 blink on */
  359.             case 5:
  360.                 iForeground |= SC_BL;
  361.                 break;
  362.  
  363.             /* 7 reverse video on */
  364.             case 7:
  365.                 iForeground |= SC_RV;
  366.                 break;
  367.  
  368.             /* 8 nondisplayed (invisible)  BUGBUG - not doing this. */
  369.  
  370.  
  371.             /* 30-37 is bit combination of 30+ red(1) green(2) blue(4)
  372.              * 30 black foreground
  373.              */
  374.             case 30:
  375.             case 31:
  376.             case 32:
  377.             case 33:
  378.             case 34:
  379.             case 35:
  380.             case 36:
  381.             case 37:
  382.                 iForeground &= ~(SC_RED|SC_GREEN|SC_BLUE);
  383.                 iForeground |= ScColorTrans[args[i]-30];
  384.                 break;
  385.  
  386.             /* 40-47 is bit combo similar to 30-37, but for background. */
  387.             case 40:
  388.             case 41:
  389.             case 42:
  390.             case 43:
  391.             case 44:
  392.             case 45:
  393.             case 46:
  394.             case 47:
  395.                 iBackground &= ~(SC_RED|SC_GREEN|SC_BLUE);
  396.                 iBackground |= ScColorTrans[args[i]-30];
  397.                 break;
  398.             }
  399.         }
  400.  
  401.         beSetTextAttributes(iForeground, iBackground);
  402.  
  403.         End += 1;
  404.         break;
  405.  
  406.  
  407.     /*
  408.      *         Set with        Esc [ Ps h
  409.      *         Reset with      Esc [ Ps l
  410.      * Mode name               Ps      Set             Reset
  411.      * -------------------------------------------------------------------
  412.      * Keyboard action         2       Locked          Unlocked
  413.      * Insertion               4       Insert          Overwrite
  414.      * Send - Receive          12      Full            Echo
  415.      * Line feed/New line      20      New line        Line feed
  416.      */
  417.     case 'h':
  418.     case 'l':
  419.     /* BUGBUG - many of the terminal modes are set with '?' as the
  420.      * first character rather than a number sign. These are dealt with
  421.      * later in this switch statement because they must be. Most other
  422.      * settings are just ignored, however.
  423.      */
  424.         End += 1;
  425.         break;
  426.  
  427.     /*         Insert line             Esc [ Pn L */
  428.     case 'L':
  429.         beInsertRow(CUR_ROW);
  430.         End += 1;
  431.         break;
  432.  
  433.     /*         Delete line             Esc [ Pn M */
  434.     case 'M':
  435.         do {
  436.             beDeleteText(CUR_ROW,LEFT_EDGE, CUR_ROW, RIGHT_EDGE);
  437.         } while (--args[0] > 0);
  438.  
  439.         End += 1;
  440.         break;
  441.  
  442.     /*         Delete character        Esc [ Pn P */
  443.     case 'P':
  444.         do {
  445.             beDeleteText(CUR_ROW, CUR_COL, CUR_ROW, CUR_COL);
  446.         } while (--args[0] > 0);
  447.         End += 1;
  448.         break;
  449.  
  450.     /*         Set the Scrolling region        Esc [ Pn(top);Pn(bot) r */
  451.     case 'r':
  452.         if (numargs == 0)
  453.             beSetScrollingRows(TOP_EDGE,BOTTOM_EDGE);
  454.         else if (numargs == 2)
  455.             beSetScrollingRows(args[0],args[1]);
  456.         End += 1;
  457.         break;
  458.  
  459.     /*         Print screen or region  Esc [ i
  460.      *         Print cursor line       Esc [ ? 1 i
  461.      *         Enter auto print        Esc [ ? 5 i
  462.      *         Exit auto print         Esc [ ? 4 i
  463.      *         Enter print controller  Esc [ 5 i
  464.      *         Exit print controller   Esc [ 4 i
  465.      */
  466.     case 'i':
  467.         /* BUGBUG - print commands are not acted upon. */
  468.         End += 1;
  469.         break;
  470.  
  471.  
  472.     /*         Clear tab at current column     Esc [ g
  473.      *         Clear all tabs                  Esc [ 3 g
  474.      */
  475.     case 'g':
  476.         if (numargs == 0)
  477.             beClearTab(CUR_COL);
  478.         else
  479.             if ((numargs == 1) && (args[0] == 3))
  480.                 beClearTab(ALL_TABS);
  481.  
  482.         End += 1;
  483.         break;
  484.  
  485.     /* BUGBUG - queries which require responses are ignored. */
  486.  
  487.     /*         Esc [ c         DA:Device Attributes
  488.      *                         or
  489.      *
  490.      *         Esc [ <sol> x   DECREQTPARM: Request Terminal Parameters
  491.      *                         * <sol> values other than 1 are ignored.  Upon
  492.      *                           receipt of a <sol> value of 1, the following
  493.      *                           response is sent:
  494.      *                                 Esc[3;<par>;<nbits>;<xspeed>;<rspeed>;1;0x
  495.      *
  496.      *                                 * Where <par>, <nbits>, <xspeed>, and <rspeed>
  497.      *                                   are as for VT100s with the following
  498.      *                                   exceptions:
  499.      *                                   <nbits>       Values of 5 and 6 bits per
  500.      *                                                 character are sent as 7 bits.
  501.      *                                   <xspeed>,<rspeed>
  502.      *                                                 These two numbers will always
  503.      *                                                 be the same.  9600 baud is
  504.      *                                                 sent for 7200 baud.
  505.      *
  506.      *         Esc [ Ps n      DSR: Device Status Report
  507.      *                         * Parameter values other than 5, 6, are ignored.
  508.      *                           If the parameter value is 5, the sequence
  509.      *                           Esc [ O n is returned.  If the parameter value is
  510.      *                           6, the CPR: Cursor Position Report sequence
  511.      *                           Esc [ Pn ; Pn R is returned with the Pn set to
  512.      *                           cursor row and column numbers.
  513.      *
  514.      * Cursor Controls:
  515.      *          ESC[#;#R                       Reports current cursor line & column
  516.      */
  517.  
  518.     /*                         spec    <esc>[<spec>h   <esc>[<spec>l
  519.      * Cursor key              ?1      Application     Cursor
  520.      * ANSI/VT52               ?2      ANSI            VT52
  521.      * Column                  ?3      132             80
  522.      * Scrolling               ?4      Smooth          Jump
  523.      * Screen                  ?5      Reverse         Normal
  524.      * Origin                  ?6      Relative        Absolute
  525.      * Wraparound              ?7      Wrap            Truncate
  526.      * Auto key repeat         ?8      Repeating       No repeat
  527.      */
  528.     case '?':
  529.         /* We didn't catch the numeric argument because the '?' stopped
  530.          * it before. Parse it now. 
  531.          */
  532.         args[0]=0;
  533.         while (isdigit(cBuffer[++End]))
  534.         {
  535.             if (End >= BufLen)
  536.                 return(Start);
  537.             args[0] = 10*args[0] + (cBuffer[End]-'0');
  538.         }
  539.  
  540.         /* If we don't handle this particular '?' command (and
  541.          * there are plenty we don't) then just ignore it. 
  542.          */
  543.         if (  (args[0] > 8) 
  544.             ||( (cBuffer[End] != 'l') && (cBuffer[End] != 'h'))
  545.            )
  546.         {
  547.             End++;
  548.             if (End >= BufLen)
  549.                 return(Start);
  550.             break;
  551.         }
  552.  
  553.         /* This command sets terminal status. Get the current status,
  554.          * determine what needs to be changed, and send it back.
  555.          */
  556.  
  557.         iMode = beGetTermMode();
  558.  
  559.         /* If we need a given mode and it's not already set, set it. */
  560.  
  561.         if ((cBuffer[End] == 'h') && (!(iMode & termAttrMode[args[0]])))
  562.         {
  563.             beSetTermMode(iMode | termAttrMode[args[0]]);
  564.         }
  565.  
  566.         /* likewise, clear it as appropriate */
  567.         if ((cBuffer[End] == 'l') && (iMode & termAttrMode[args[0]]))
  568.         {
  569.             beSetTermMode(iMode & ~termAttrMode[args[0]]);
  570.         }
  571.  
  572.         End++;
  573.         break;
  574.  
  575.     /* If it's an escape sequence we don't treat, pretend as though we never saw
  576.      * it.
  577.      */
  578.     default:
  579.         End += 1;
  580.         break;
  581.     }
  582.  
  583.     return(End);
  584.  
  585. }
  586.  
  587.  
  588. /* ProcessEscape - 
  589.  * 
  590.  * At this point, <esc> has been seen. Start points to the escape
  591.  * itself, and then this routine is responsible for parsing the
  592.  * rest of the escape sequence, and either pawning off further parsing,
  593.  * or acting upon it as appropriate.
  594.  *
  595.  * Note that the escape sequences being parsed are still contained in cBuffer.
  596.  */
  597.  
  598. static int ProcessEscape(int Start)
  599. {
  600.     int End;
  601.     int left;
  602.     int fore, back;
  603.     int i;
  604.  
  605.     /* if it's definitely not a full escape sequence, return that we haven't
  606.      * seen it.
  607.      */
  608.     if ((cBuffer[Start] != 27)||(Start+1 >= BufLen))
  609.         return(Start);
  610.  
  611.     End = Start + 1;
  612.     
  613.     /* At this point, if the sequence is <esc> x, 'End' points at
  614.      * x
  615.      */
  616.  
  617.     /* left = number of characters left unparsed in the buffer. */
  618.     left =  BufLen - End -1;
  619.  
  620.     /* Main switch statement - parse the escape sequence according to the
  621.      * next character we see.
  622.      */
  623.  
  624.     switch (cBuffer[End])
  625.     {
  626.     /*         Hard Reset                      Esc c    BUGBUG - not imp'd. */
  627.     case 'c':
  628.         End += 1;
  629.         break;
  630.  
  631.     /*         Cursor up               Esc A */
  632.     case 'A':
  633.         beOffsetCursor(-1,0);
  634.         End += 1;
  635.         break;
  636.  
  637.     /*         Cursor down             Esc B */
  638.     case 'B':
  639.         beOffsetCursor(1,0);
  640.         End += 1;
  641.         break;
  642.  
  643.     /*         Cursor right            Esc C */
  644.     case 'C':
  645.         beOffsetCursor(0,1);
  646.         End += 1;
  647.         break;
  648.  
  649.     /*         Cursor left             Esc D */
  650.     case 'D':
  651.         beOffsetCursor(0,-1);
  652.         End += 1;
  653.         break;
  654.  
  655.     /*         Newline command:       Esc E */
  656.     case 'E':
  657.         beRawTextOut("\n",strlen("\n"));
  658.         End += 1;
  659.         break;
  660.  
  661.     /*          Invoke the Graphics character set  Esc F */
  662.     case 'F':
  663.         beGetTextAttributes(&fore, &back);
  664.         if (! (fore & SC_GRAPHICS))
  665.         {
  666.             fore &= ~(SC_ASCII|SC_G0|SC_G1);
  667.             fore |= SC_GRAPHICS;
  668.             beSetTextAttributes(fore, back);
  669.         }
  670.         End += 1;
  671.         break;
  672.  
  673.     /*         Invoke the ASCII character set     Esc G */
  674.     case 'G':
  675.         beGetTextAttributes(&fore, &back);
  676.         if (! (fore & SC_ASCII))
  677.         {
  678.             fore &= ~(SC_G0|SC_G1|SC_GRAPHICS);
  679.             fore |= SC_ASCII;
  680.             beSetTextAttributes(fore, back);
  681.         }
  682.         End += 1;
  683.         break;
  684.  
  685.     /*          Move the cursor to (1,1): Home cursor             Esc H */
  686.     case 'H':
  687.         beAbsoluteCursor(TOP_EDGE,LEFT_EDGE);
  688.         End += 1;
  689.         break;
  690.  
  691.     /*         Reverse line feed       Esc I */
  692.     case 'I':
  693.         beOffsetCursor(-1,0);
  694.         End += 1;
  695.         break;
  696.  
  697.     /*         Erase to end of screen  Esc J   */
  698.     case 'J':
  699.         beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE);
  700.         End += 1;
  701.         break;
  702.  
  703.     /*         Erase to end of line    Esc K */
  704.     case 'K':
  705.         beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE);
  706.         End += 1;
  707.         break;
  708.  
  709.     /*         Reverse Line:   Esc M */
  710.     case 'M':
  711.         beAbsoluteCursor(CUR_ROW, LEFT_EDGE);
  712.         beOffsetCursor(-1,0);
  713.         End += 1;
  714.         break;
  715.  
  716.     /* Switch to G1 graphics character set.         Esc N */
  717.     case 'N':
  718.         beGetTextAttributes(&fore, &back);
  719.         if (! (fore & SC_G1))
  720.         {
  721.             fore &= ~(SC_G0|SC_ASCII|SC_GRAPHICS);
  722.             fore |= SC_G1;
  723.             beSetTextAttributes(fore, back);
  724.         }
  725.         End += 1;
  726.         break;
  727.  
  728.     /* Switch to G0 graphics character set        Esc O */
  729.     case 'O':
  730.         beGetTextAttributes(&fore, &back);
  731.         if (! (fore & SC_G0))
  732.         {
  733.             fore &= ~(SC_G1|SC_ASCII|SC_GRAPHICS);
  734.             fore |= SC_G0;
  735.             beSetTextAttributes(fore, back);
  736.         }
  737.         End += 1;
  738.         break;
  739.  
  740.     /*         Print cursor line       Esc V   BUGBUG - unimp'd */
  741.     case 'V':
  742.         End += 1;
  743.         break;
  744.  
  745.     /*          Enter print controller  Esc W  BUGBUG - unimp'd */
  746.     case 'W':
  747.         End += 1;
  748.         break;
  749.  
  750.     /*         Exit print controller   Esc X    BUGBUG - unimp'd */
  751.     case 'X':
  752.         End += 1;
  753.         break;
  754.  
  755.     /*         Cursor address          Esc Y row col BUGBUG - unimp'd and needed */
  756.     case 'Y':
  757.         End += 1;
  758.         break;
  759.  
  760.     /*         Identify terminal type     Esc Z    */
  761.     case 'Z':
  762.         beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE));
  763.         End += 1;
  764.         break;
  765.  
  766.     /* One of dozens of escape sequences starting <esc>[. Parse further */
  767.     case '[':
  768.         /* pass in the escape as the starting point */
  769.         End = ProcessBracket(End-1);
  770.         break;
  771.  
  772.     /*        Print screen            Esc ]     BUGBUG - unimp'd */
  773.     case ']':
  774.         End += 1;
  775.         break;
  776.  
  777.     /*         Enter auto print        Esc ^    BUGBUG - unimpd' */
  778.     case '^':
  779.         End += 1;
  780.         break;
  781.  
  782.     /*         Exit auto print         Esc -    BUGBUG - unimpd' */
  783.     case '-':
  784.         End += 1;
  785.         break;
  786.  
  787.     /*         Alternate keypad        Esc =    BUGBUG - unimpd' */
  788.     case '=':
  789.         End += 1;
  790.         break;
  791.  
  792.     /*         Numeric keypad          Esc >    BUGBUG - unimpd' */
  793.     case '>':
  794.         End += 1;
  795.         break;
  796.  
  797.     /*         Enter ANSI mode         Esc <    BUGBUG - unimpd' */
  798.     case '<':
  799.         End += 1;
  800.         break;
  801.  
  802.     /*         Save cursor position & Attributes:     Esc 7 */
  803.     case '7':
  804.         beSaveCursor();
  805.         End += 1;
  806.         break;
  807.  
  808.     /*         Restore cursor position & attributes:  Esc 8 */
  809.     case '8':
  810.         beRestoreCursor();
  811.         End += 1;
  812.         break;
  813.  
  814.     /*  Set character size - BUGBUG - unimp'd.
  815.      * # 1             Double ht, single width top half chars
  816.      * # 2             Double ht, single width lower half chars
  817.      * # 3             Double ht, double width top half chars
  818.      * # 4             Double ht, double width lower half chars
  819.      * # 5             Single ht, single width chars
  820.      * # 6             Single ht, double width chars    
  821.      */
  822.     case '#':
  823.         End += 1;
  824.         break;
  825.  
  826.     /* Select character set
  827.      * ESC ( A             British 
  828.      * ESC ( C             Finnish
  829.      * ESC ( E             Danish or Norwegian
  830.      * ESC ( H             Swedish
  831.      * ESC ( K             German
  832.      * ESC ( Q             French Canadian
  833.      * ESC ( R             Flemish or French/Belgian
  834.      * ESC ( Y             Italian
  835.      * ESC ( Z             Spanish
  836.      * ESC ( 1             Alternative Character
  837.      * ESC ( 4             Dutch
  838.      * ESC ( 5             Finnish
  839.      * ESC ( 6             Danish or Norwegian
  840.      * ESC ( 7             Swedish
  841.      * ESC ( =             Swiss (French or German)
  842.      */
  843.     case '(':
  844.     case ')':
  845.         /* BUGBUG - most character sets aren't implemented. */
  846.         beGetTextAttributes(&fore, &back);
  847.         switch (cBuffer[++End])
  848.         {
  849.         case 'B':    /* ESC ( B             North American ASCII set */
  850.             i=SC_ASCII;
  851.             break;
  852.  
  853.         case '0':    /* ESC ( 0             Line Drawing */
  854.             i=SC_G1;
  855.             break;
  856.  
  857.         case '2':    /* ESC ( 2             Alternative Line drawing */
  858.             i=SC_G0;
  859.             break;
  860.  
  861.         default:
  862.             /* Make sure the screen mode isn't set. */
  863.             i = 0xFFFFFFFF;
  864.             break;
  865.         }
  866.  
  867.         if (! (fore & i))
  868.         {
  869.             fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
  870.             fore |= i;
  871.             beSetTextAttributes(fore, back);
  872.         }
  873.  
  874.         End += 1;
  875.         break;
  876.  
  877.     /* Unknown escape sequence */
  878.     default:
  879.         {
  880.             char cbuf[80];
  881.             sprintf(cbuf,"<esc>%c", cBuffer[End]);
  882.             beRawTextOut(cbuf+Start,6);
  883.             End += 1;
  884.         }
  885.     }
  886.  
  887.     return(End);
  888. }
  889.  
  890.  
  891. /* ProcessControl -
  892.  *
  893.  * Process a probable escape or control sequence
  894.  * starting at the supplied index. Return the index
  895.  * of the first character *after* the escape sequence we
  896.  * process.
  897.  * In the case of an incomplete sequence, ie one
  898.  * that isn't completed by the end of the buffer, return
  899.  * 'Start'.
  900.  * In the case of an invalid sequence,
  901.  * note the invalid escape sequence, and return Start+1
  902.  */
  903.  
  904. static int ProcessControl(int Start)
  905. {
  906.     int fore, back;
  907.     int End = Start;
  908.  
  909.     /* Check to make sure we at least have enough characters
  910.      * for a control character
  911.      */
  912.  
  913.     if (Start >= BufLen)
  914.         return(Start);
  915.  
  916.     switch (cBuffer[Start])
  917.     {
  918.     /*  NUL     0       Fill character; ignored on input.
  919.      *  DEL     127     Fill character; ignored on input.
  920.      */
  921.     case 0:
  922.     case 127:
  923.         End += 1;
  924.         break;
  925.  
  926.     /* ENQ     5       Transmit answerback message. */
  927.     case 5:
  928.         beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE));
  929.         End += 1;
  930.         break;
  931.  
  932.     /* BEL     7       Ring the bell. */
  933.     case 7:
  934.         beRingBell();
  935.         End += 1;
  936.         break;
  937.  
  938.     /* BS      8       Move cursor left. */
  939.     case 8:
  940.         beOffsetCursor(0,-1);
  941.         End += 1;
  942.         break;
  943.  
  944.     /* HT      9       Move cursor to next tab stop. */
  945.     case 9:
  946.         beAdvanceToTab();
  947.         End += 1;
  948.         break;
  949.  
  950.     /* LF      10      Line feed; causes print if in autoprint. */
  951.     case 10:
  952.         beOffsetCursor(1,0);
  953.         End += 1;
  954.         break;
  955.  
  956.     /* VT      11      Same as LF. 
  957.      * FF      12      Same as LF.
  958.      */
  959.     case 11:
  960.     case 12:
  961.         beOffsetCursor(1,0);
  962.         End += 1;
  963.         break;
  964.  
  965.     /* CR      13      Move cursor to left margin or newline. */
  966.     case 13:
  967.         beOffsetCursor(1,0);
  968.         beAbsoluteCursor(CUR_ROW,LEFT_EDGE);
  969.         End += 1;
  970.         break;
  971.  
  972.     /* SO      14      Invoke G1 character set. */
  973.     case 14:
  974.         beGetTextAttributes(&fore, &back);
  975.         if (! (fore & SC_G1))
  976.         {
  977.             fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
  978.             fore |= SC_G1;
  979.             beSetTextAttributes(fore, back);
  980.         }
  981.         End += 1;
  982.         break;
  983.  
  984.     /* SI      15      Invoke G0 character set. */
  985.     case 15:
  986.         beGetTextAttributes(&fore, &back);
  987.         if (! (fore & SC_G0))
  988.         {
  989.             fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
  990.             fore |= SC_G0;
  991.             beSetTextAttributes(fore, back);
  992.         }
  993.         End += 1;
  994.         break;
  995.  
  996.     /* CAN     24      Cancel escape sequence and display checkerboard. BUGBUG - not imp'd.
  997.      * SUB     26      Same as CAN.
  998.      */
  999.     case 24:
  1000.         End += 1;
  1001.         break;
  1002.  
  1003.     /* ESC     27      Introduce a control sequence. */
  1004.     case 27:
  1005.         End = ProcessEscape(Start);
  1006.         break;
  1007.  
  1008.     /* Print any other control character received. */
  1009.     default:
  1010.         {
  1011.             char buf[4];
  1012.             sprintf(buf,"^%c",'A' + cBuffer[Start] - 1);
  1013.             beRawTextOut(buf, 2);
  1014.             End += 1;
  1015.         }
  1016.         break;
  1017.     }
  1018.  
  1019.     return(End);
  1020. }
  1021.  
  1022.  
  1023. /* ProcessBuffer -
  1024.  * 
  1025.  * Process the current contents of the terminal character buffer.
  1026.  */
  1027. static int ProcessBuffer(void)
  1028. {
  1029.     int Start=0,End=0;
  1030.  
  1031.     /* Null-terminate the buffer. Why? Heck, why not? */
  1032.  
  1033.     cBuffer[BufLen] = '\0'; 
  1034.  
  1035.     /* Loop through the entire buffer. Start will be incremented
  1036.      * to point at the start of unprocessed text in the buffer as
  1037.      * we go.
  1038.      */
  1039.     while (Start < BufLen)
  1040.     {
  1041.         End = Start;
  1042.  
  1043.         /* Since we null-terminated, null < 27 and we have a termination condition */
  1044.         while ((cBuffer[End] > 27)||(cBuffer[End] == 10)||(cBuffer[End] == 13))
  1045.             End++;
  1046.  
  1047.         /* At this point, if Start < End, we have a string of characters which
  1048.          * doesn't have any control sequences, and should be printed raw.
  1049.          */
  1050.  
  1051.         if (End > Start)
  1052.             beRawTextOut(cBuffer+Start, End-Start);
  1053.  
  1054.         if (End >= BufLen)
  1055.         {
  1056.             break;
  1057.         }
  1058.  
  1059.         /* At this point, 'End' points to the beginning of an escape
  1060.          * sequence. We'll reset 'start' to be AFTER parsing the
  1061.          * escape sequence. Note that if the escape sequence
  1062.          * is incomplete, ProcessControl should return the
  1063.          * same value passed in. Otherwise, it'll return the
  1064.          * index of the first character after the valid
  1065.          * escape sequence.
  1066.          */
  1067.  
  1068.         Start = ProcessControl(End);
  1069.  
  1070.         if (Start == End)
  1071.         {
  1072.             /* The escape sequence was incomplete.
  1073.              * Move the unprocessed portion of the input buffer
  1074.              * to start at the beginning of the buffer, then 
  1075.              * return.
  1076.              */
  1077.  
  1078.             while (End < BufLen)
  1079.             {
  1080.                 cBuffer[End-Start] = cBuffer[End];
  1081.                 End += 1;
  1082.             }
  1083.  
  1084.             BufLen = End-Start;
  1085.             return(0);
  1086.         }
  1087.     }
  1088.  
  1089.     /* If we made it this far, Start == Buflen, and so we've finished
  1090.      * processing the buffer completely.
  1091.      */
  1092.     BufLen = 0;
  1093.     cBuffer[BufLen] = '\0';
  1094.          
  1095.     return(0);
  1096. }
  1097.  
  1098.  
  1099. /* vtProcessedTextOut -
  1100.  *
  1101.  * Output characters to terminal, passing them through the vt100 emulator.
  1102.  * Return -1 on error 
  1103.  */
  1104. int 
  1105. vtProcessedTextOut(char *cbuf, int count)
  1106. {
  1107.     /* If we have a buffer overflow, error out if we haven't already crashed. */
  1108.  
  1109.     if ((count + BufLen) > MAXVTBUFLEN)
  1110.     {
  1111.         beRawTextOut("ERROR: VT-100 internal buffer overflow!",39);
  1112.         return(-1);
  1113.     }
  1114.  
  1115.     /* Otherwise, add our requested information to the
  1116.      * output buffer, and attempt to parse it.
  1117.      */
  1118.  
  1119.     memcpy(cBuffer + BufLen, cbuf, count);
  1120.     BufLen += count;
  1121.  
  1122.     return(ProcessBuffer());
  1123. }
  1124.  
  1125.